package com.chagee.store.operation.server.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.chagee.lark.anno.ExceptionLarkNotice;
import com.chagee.lark.enums.UrgentTypeEnum;
import com.chagee.store.operation.api.bo.OrderExtJsonBO;
import com.chagee.store.operation.api.bo.SettlementExtJsonBO;
import com.chagee.store.operation.api.enums.SettlementStatusEnum;
import com.chagee.store.operation.api.enums.SettlementTypeEnum;
import com.chagee.store.operation.api.enums.StoreWorkOrderStatusEnum;
import com.chagee.store.operation.common.constant.CommonConstant;
import com.chagee.store.operation.common.exception.BizErrorCode;
import com.chagee.store.operation.common.exception.BizException;
import com.chagee.store.operation.server.entity.StoreConfigurationDO;
import com.chagee.store.operation.server.entity.StoreSettlementDO;
import com.chagee.store.operation.server.entity.WorkOrderDO;
import com.chagee.store.operation.server.event.dao.StoreConfigurationMapper;
import com.chagee.store.operation.server.event.dao.StoreSettlementMapper;
import com.chagee.store.operation.server.event.dao.WorkOrderMapper;
import com.chagee.store.operation.server.event.dao.WorkOrderReceiverMapper;
import com.chagee.store.operation.server.event.service.*;
import com.chagee.store.operation.server.generator.codeGenerator.SettlementNoCodeGenerator;
import com.chagee.store.operation.server.model.bo.OrderPenalizeBO;
import com.chagee.store.operation.server.model.bo.StoreInfoBO;
import com.chagee.store.operation.server.model.param.QueryStoreParam;
import com.chagee.store.operation.server.service.base.StoreBaseService;
import com.chagee.store.operation.server.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author : hk
 * @Date : 2024/10/22
 */

@Slf4j
@Service
public class GenerateStoreSettlementData {

	@Resource
	private WorkOrderBaseService workOrderBaseService;
	@Resource
	private WorkOrderMapper workOrderMapper;

	@Resource
	private WorkOrderReceiverMapper workOrderReceiverMapper;
	@Resource
	private TaskExecutionBaseService taskExecutionBaseService;
	@Resource
	private StoreConfigurationMapper storeConfigurationMapper;
	@Resource
	private StoreBaseService storeBaseService;
	@Resource
	private SettlementNoCodeGenerator settlementNoCodeGenerator;
	@Resource
	private StoreSettlementMapper storeSettlementMapper;
	@Resource
	private StoreSettlementBaseService storeSettlementBaseService;

	@Value("${settlement.monthDays:30.5}")
	private Double monthDays;

	@Value("${settlement.notClosedPenalize:500}")
	private Integer notClosedPenalize;


	@ExceptionLarkNotice(bizType = "生成打烊结算单", title = "创建结算单", urgentType = UrgentTypeEnum.APP_PHONE)
	public Boolean generateData(StoreInfoBO storeDO, String cycle) throws Exception {
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM");
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date lastMonth = StringUtils.isBlank(cycle) ? getLastMonth() : sdf.parse(cycle);

		log.info("上月时间generateData_lastMonth: {}", JSONObject.toJSONString(lastMonth));
		LambdaQueryWrapper<StoreConfigurationDO> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(StoreConfigurationDO::getStoreCode, storeDO.getStoreNo());
		List<StoreConfigurationDO> storeConfigurationDOS = storeConfigurationMapper.selectList(queryWrapper);
		if (CollectionUtils.isEmpty(storeConfigurationDOS)) {
			log.info("门店未配置结算配置信息: {}", JSONObject.toJSONString(storeDO.getStoreNo()));
			return false;
		}
		StoreConfigurationDO storeConfigurationDO = storeConfigurationDOS.stream().findFirst().get();
		if (Objects.isNull(storeConfigurationDO.getFloorPrice())) {
			log.info("门店未配置结算底价: {}", JSONObject.toJSONString(storeDO.getStoreNo()));
			return false;
		}
		log.info("门店打烊配置信息: {}", JSONObject.toJSONString(storeConfigurationDO));
		if (Objects.isNull(storeConfigurationDO.getBillingStartDate())) {
			return false;
		}
		BigDecimal outArea = new BigDecimal(StringUtils.isNotBlank(storeDO.getOutArea()) ? storeDO.getOutArea() : "0");
		BigDecimal inDoorArea = new BigDecimal(StringUtils.isNotBlank(storeDO.getInDoorArea()) ? storeDO.getInDoorArea() : "0");

		Integer manualLineNum = ObjectUtils.isNotEmpty(storeDO.getManualLineNum()) ? storeDO.getManualLineNum() : 0;
		Integer autoLineNum = ObjectUtils.isNotEmpty(storeDO.getAutoLineNum()) ? storeDO.getAutoLineNum() : 0;
		Integer manualAutoLineNum = ObjectUtils.isNotEmpty(storeDO.getManualAutoLineNum()) ? storeDO.getManualAutoLineNum() : 0;
		int lineNumSum = Stream.of(manualLineNum, autoLineNum, manualAutoLineNum).mapToInt(Integer::intValue).sum();

		Date startOfMonth = DateUtil.getStartOfMonth(lastMonth);
		Date endOfMonth = DateUtil.getEndOfMonth(lastMonth);

		if (cn.hutool.core.date.DateUtil.compare(storeConfigurationDO.getBillingStartDate(), startOfMonth)>0) {
			startOfMonth = storeConfigurationDO.getBillingStartDate();
		}
		List<WorkOrderDO> workOrderDOList = workOrderMapper.selectLastMonth(storeDO.getStoreNo(), DateUtil.format(startOfMonth, DateUtil.DEFAULT_DATE_PATTERN), DateUtil.format(endOfMonth, DateUtil.DEFAULT_DATE_PATTERN));

		int monthNum = StringUtils.isBlank(cycle) ? getLastMonthNum() : getMonthNum(simpleDateFormat.format(lastMonth));
		if (CollectionUtils.isNotEmpty(workOrderDOList)) {
			log.info("workOrderDOList{}", JSONObject.toJSONString(workOrderDOList));
			Map<String, List<WorkOrderDO>> listMap = workOrderDOList.stream().filter(s -> StringUtils.isNotBlank(s.getSupplierNo())).collect(Collectors.groupingBy(WorkOrderDO::getSupplierNo));
			//上月工单都没有供应商接单情况
			if (MapUtil.isEmpty(listMap)) {
				log.info("上月工单都没有供应商接单情况:{}", JSONObject.toJSONString(storeDO.getStoreNo()));
				return true;
			}
			// 结算单周期 的 上个月
			Map<String, StoreSettlementDO> frontMap = new HashMap<>();
			Date frontMonth = DateUtil.getFrontMonth(lastMonth);
			List<StoreSettlementDO> frontSettlementList = storeSettlementBaseService.selectSettlement(storeDO.getStoreNo(), frontMonth);
			if (CollectionUtil.isNotEmpty(frontSettlementList)) {
				frontMap = frontSettlementList.stream().collect(Collectors.toMap(StoreSettlementDO::getSupplierNo, Function.identity(), (a, b) -> a));
			}

			for (Map.Entry<String, List<WorkOrderDO>> entry : listMap.entrySet()) {
				//门店 + 月份 + 供应商
				LambdaQueryWrapper<StoreSettlementDO> settlementWrapper = new LambdaQueryWrapper<>();
				settlementWrapper.eq(StoreSettlementDO::getStoreNo, storeDO.getStoreNo());
				settlementWrapper.eq(StoreSettlementDO::getSupplierNo, entry.getKey());
				settlementWrapper.eq(StoreSettlementDO::getSettlementCycle, lastMonth);
				settlementWrapper.eq(StoreSettlementDO::getDeleteFlag, NumberUtils.INTEGER_ZERO);
				List<StoreSettlementDO> storeSettlementDOS = storeSettlementMapper.selectList(settlementWrapper);
				List<StoreSettlementDO> collect = storeSettlementDOS.stream().filter(s -> s.getDocumentStatus().equals(SettlementStatusEnum.WAIT_ISSUED.getType())).collect(Collectors.toList());
				//初始状态逻辑删除
				if (CollectionUtils.isNotEmpty(collect)) {
					storeSettlementBaseService.updateDeleteStoreSettlement(collect);
				}else if (CollectionUtils.isNotEmpty(storeSettlementDOS)) {
					continue;
				}

				//BigDecimal roundedNum = BigDecimal.ZERO;
				log.info("供应商编码: {},开始生成结算单", JSONObject.toJSONString(entry.getKey()));
				Double average = entry.getValue().stream().filter(s -> StringUtils.isNotBlank(s.getDocumentScore())).map(WorkOrderDO::getDocumentScore).mapToInt(Integer::parseInt).average().orElse(0.0);
				BigDecimal bd = new BigDecimal(Double.toString(average));
				// 结算平均分
				BigDecimal roundedNum = bd.setScale(0, RoundingMode.HALF_UP);
				// 供应商
				String suppliers = entry.getValue().get(0).getSupplier();

				StoreSettlementDO settlementDO = new StoreSettlementDO();
				settlementDO.setStoreNo(storeDO.getStoreNo());
				settlementDO.setStoreName(storeDO.getStoreName());
				settlementDO.setStoreLineNum(ObjectUtils.isNotEmpty(storeConfigurationDO.getStoreLineNum()) ? storeConfigurationDO.getStoreLineNum() : lineNumSum);
				settlementDO.setStoreArea(StringUtils.isNotBlank(storeConfigurationDO.getStoreArea()) ? storeConfigurationDO.getStoreArea() : String.valueOf(outArea.add(inDoorArea).doubleValue()));
				settlementDO.setStoreType(storeDO.getStoreType());
				settlementDO.setSettlementType(SettlementTypeEnum.THREE_CLEAN.getType());
				settlementDO.setSettlementCycle(lastMonth);
				settlementDO.setDocumentStatus(SettlementStatusEnum.WAIT_ISSUED.getType());
				settlementDO.setSettlementScore(roundedNum);
				settlementDO.setSupplier(suppliers);
				settlementDO.setSupplierNo(entry.getKey());
				OrderPenalizeBO orderPenalizeBO = getPayMoney(storeDO, entry.getValue(), storeConfigurationDO, monthNum);
				//上月该供应商结算单 支付金额存在负数
				BigDecimal frontPayMoney = BigDecimal.ZERO;
				if (MapUtil.isNotEmpty(frontMap) && Objects.nonNull(frontMap.get(entry.getKey()))) {
					StoreSettlementDO front = frontMap.get(entry.getKey());
					if (front.getPayMoney().compareTo(BigDecimal.ZERO) < 0) {
						frontPayMoney = front.getPayMoney();
						SettlementExtJsonBO extJsonBO = new SettlementExtJsonBO();
						Map<String, Object> remarkMap = new HashMap<>();
						remarkMap.put("frontMonth", simpleDateFormat.format(frontMonth));
						remarkMap.put("adjustMoney", front.getPayMoney());
						String remark = StrUtil.format(CommonConstant.SETTLEMENT_DEFAULT_REMARK, remarkMap);
						extJsonBO.setRemark(remark);
						settlementDO.setExtra(JSON.toJSONString(extJsonBO));
						settlementDO.setAdjustMoney(front.getPayMoney());
						orderPenalizeBO.setFrontMoney(front.getPayMoney());
					}
				}
				BigDecimal resultMoney = orderPenalizeBO.getPayMoney().add(frontPayMoney);
				log.info("供应商编码: {},扣罚明细{}", JSONObject.toJSONString(entry.getKey()), JSONObject.toJSONString(orderPenalizeBO));
				settlementDO.setPayMoney(resultMoney.setScale(0, RoundingMode.HALF_UP)); //结算金额四舍五入
				settlementDO.setPenalizeMoney(JSON.toJSONString(orderPenalizeBO));
				settlementDO.setStoreCategory(storeDO.getStoreCategory());
				settlementDO.setCompanyId(storeDO.getCompanyId());
				settlementDO.setCompanyName(storeDO.getCompanyName());
				settlementDO.setBusinessId(storeDO.getBusinessId());
				settlementDO.setBusinessName(storeDO.getBusinessName());

				//支付金额为0 , 不生成结算单
				//if (orderPenalizeBO.getPayMoney().compareTo(BigDecimal.ZERO) != 0) {
				settlementDO.setSettlementNo(settlementNoCodeGenerator.generate());
				storeSettlementMapper.insert(settlementDO);
				//}
			}

			// 结算底价
			updateWorkOrder(workOrderDOList, storeConfigurationDO.getFloorPrice());
		}
		return true;
	}


	/**
	 * (稽核扣罚 , 其他扣罚) = 工单累计扣除罚金总和
	 * 未打烊扣罚 = 500*已过期状态工单
	 * 合格率扣罚 = 底价/当月天数 * 不合格工单(评分<80)个数
	 * 支付金额 = 门店设置的结算底价 - 未打烊扣罚 - 合格率扣罚 - 离职人员扣罚(待定) -稽核扣罚 - 其他扣罚
	 *
	 * @return
	 */
	public OrderPenalizeBO getPayMoney(StoreInfoBO storeDO, List<WorkOrderDO> workOrderDOList, StoreConfigurationDO storeConfigurationDO, int lastMonthNum) {
		//结算底价
		BigDecimal floorPrice = storeConfigurationDO.getFloorPrice();

		//int lastMonthNum = getLastMonthNum();
		BigDecimal bigDecimal = new BigDecimal(String.valueOf(lastMonthNum));
		BigDecimal labourPrice = floorPrice.divide(bigDecimal, 2, BigDecimal.ROUND_HALF_UP); // 底价/当月天数, 保留2位小数
		log.info("门店: {},结算底价:{}", storeDO.getStoreNo(),JSONObject.toJSONString(labourPrice));
		List<WorkOrderDO> collect = workOrderDOList.stream().filter(s -> StoreWorkOrderStatusEnum.COMPLETED.getType().equals(s.getDocumentStatus()) && StringUtils.isNotBlank(s.getDocumentScore()) && Integer.parseInt(s.getDocumentScore()) >= 80).collect(Collectors.toList());
		//应付金额
		BigDecimal decimal = labourPrice.multiply(new BigDecimal(String.valueOf(collect.size())));
		if (collect.size() == lastMonthNum) {
			decimal = floorPrice;
		}

		OrderPenalizeBO orderPenalizeBO = getPenalizeMoney(workOrderDOList, labourPrice);
		orderPenalizeBO.setCompletedOrderSize(collect.size());
		orderPenalizeBO.setFloorPrice(floorPrice);
		BigDecimal penalizeSum = orderPenalizeBO.getTotalPenalize();
//		if (decimal.compareTo(penalizeSum) < 0) {
//			orderPenalizeBO.setPayMoney(BigDecimal.ZERO);
//		} else {
		BigDecimal resultMoney = decimal.subtract(penalizeSum);
		orderPenalizeBO.setPayMoney(resultMoney);
		//}
		return orderPenalizeBO;

	}

	/**
	 *
	 * @param workOrderDOList
	 * @param labourPrice 每天底价
	 * @return
	 */
	public OrderPenalizeBO getPenalizeMoney(List<WorkOrderDO> workOrderDOList, BigDecimal labourPrice) {

		//BigDecimal passRate = BigDecimal.ZERO;
		BigDecimal notClosed = BigDecimal.ZERO;
		BigDecimal penalizeMoneySum = BigDecimal.ZERO;
		if (CollectionUtils.isNotEmpty(workOrderDOList)) {
			//合格率扣罚
//			List<WorkOrderDO> noOrderList = workOrderDOList.stream().filter(s -> StringUtils.isNotBlank(s.getDocumentScore()) && Integer.parseInt(s.getDocumentScore()) < 80).collect(Collectors.toList());
//			if (CollectionUtils.isNotEmpty(noOrderList)) {
//				passRate = labourPrice.multiply(new BigDecimal(noOrderList.size()));
//			}
			//未打烊扣罚 (过期;过期申述中)
			List<WorkOrderDO> expireOrder = workOrderDOList.stream().filter(s -> StoreWorkOrderStatusEnum.EXPIRED.getType().equals(s.getDocumentStatus()) || StoreWorkOrderStatusEnum.EXPIRED_APPEAL.getType().equals(s.getDocumentStatus())).collect(Collectors.toList());
			if (CollectionUtils.isNotEmpty(expireOrder)) {
				notClosed = new BigDecimal(String.valueOf(notClosedPenalize)).multiply(new BigDecimal(String.valueOf(expireOrder.size())));
			}

			//(稽核扣罚 , 其他扣罚)
			penalizeMoneySum = workOrderDOList.stream().filter(s -> ObjectUtils.isNotEmpty(s.getPenalizeMoney())).map(WorkOrderDO::getPenalizeMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
		}
		log.info("notClosed:{}, penalizeMoneySum:{}",notClosed,penalizeMoneySum);
		BigDecimal penalizeSum = Stream.of(notClosed, penalizeMoneySum).reduce(BigDecimal.ZERO, BigDecimal::add);

		OrderPenalizeBO penalizeBO = new OrderPenalizeBO();
		//penalizeBO.setPassRate(passRate);
		penalizeBO.setNotClosed(notClosed);
		penalizeBO.setOtherPenalizeSum(penalizeMoneySum);
		penalizeBO.setTotalPenalize(penalizeSum);
		return penalizeBO;
	}


	//周期上月
	private Date getLastMonth() {
		SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM");
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.MONTH, -1);
		String format = sd.format(calendar.getTime());
		Date lastMonth = null;
		try {
			lastMonth = sd.parse(format);
		} catch (ParseException e) {
			throw new BizException(BizErrorCode.DATE_CONVERT_FAIL);
		}
		return lastMonth;
	}

	//上月天数
	public int getLastMonthNum() {
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.MONTH, -1);
		calendar.set(Calendar.DAY_OF_MONTH, 1);
		int lastDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
		return lastDay;
	}

	public int getMonthNum(String dateStr) {
		Integer year = Integer.valueOf(dateStr.substring(0, 4));
		Integer month = Integer.valueOf(dateStr.substring(5, 7));
		LocalDate date = LocalDate.of(year, month, 1);
		return date.lengthOfMonth();
	}

	private void updateWorkOrder(List<WorkOrderDO> workOrderDOList, BigDecimal floorPrice) {
		OrderExtJsonBO extJsonBO = new OrderExtJsonBO();
		for (WorkOrderDO workOrderDO : workOrderDOList) {
			if (StringUtils.isNotBlank(workOrderDO.getExtra())) {
				extJsonBO = JSON.parseObject(workOrderDO.getExtra(), new TypeReference<OrderExtJsonBO>() {
				});
			}
			extJsonBO.setFloorPrice(floorPrice);
			workOrderBaseService.updateWorkOrder(workOrderDO.getWorkOrderNumber(), extJsonBO);
		}
	}

	public Boolean generateByStoreCode(String storeCode) {
		Date lastMonth = getLastMonth();
		log.info("上月时间generateByStoreCode: {}", JSONObject.toJSONString(lastMonth));
		LambdaQueryWrapper<StoreSettlementDO> queryWrapper = new LambdaQueryWrapper();
		queryWrapper.eq(StoreSettlementDO::getStoreNo, storeCode);
		queryWrapper.eq(StoreSettlementDO::getSettlementCycle, lastMonth);
		queryWrapper.eq(StoreSettlementDO::getDeleteFlag, NumberUtils.INTEGER_ZERO);
		List<StoreSettlementDO> list = storeSettlementMapper.selectList(queryWrapper);
		if (CollectionUtils.isNotEmpty(list)) {
			storeSettlementMapper.deleteBatchIds(list.stream().map(StoreSettlementDO::getId).collect(Collectors.toList()));
		}

		/*QueryStoreParam queryStoreParam = new QueryStoreParam();
		queryStoreParam.setClosingTaskStatus(NumberUtils.INTEGER_ONE);
		queryStoreParam.setStoreNo(storeCode);*/
		/*List<StoreInfoBO> storeDOS = storeBaseService.queryStoreList(queryStoreParam);
		StoreInfoBO storeInfoBO = storeDOS.stream().findFirst().orElse(null);
		Boolean aBoolean = generateData(storeInfoBO);*/
		return true;
	}
}
